[AD] Scalaアプリケーションの開発・保守は合同会社ミルクソフトにお任せください
この記事では、Scala 3(コードネーム:Dotty)から使用できるようになった「トレイトパラメータ」について解説します。
Scala 3ではトレイトにパラメータを持たせることができる
Scala 3からは、トレイトにパラメータを持たせることができるようになりました。
trait Result(val statusCode: Int, val statusCodeName: String) { override def toString = s"$statusCode $statusCodeName" def body: String }
このパラメータを「トレイトパラメータ」と呼びます。
また、トレイトパラメータを持つトレイトについての呼称はまだ定まっていないため、Scalapediaでは「パラメータ付きトレイト」と呼ぶこととします。
パラメータ付きトレイトは普通のトレイトと同様に使用できる
パラメータ付きトレイトは、普通のトレイトと同様に宣言し、使用することができます。
class ImATeapot(date: Date) extends Result(418, "I'm a teapot") { def body = "Sorry, I'm a teapot." } val result = new ImATeapot(Date.now) println(result.body) println(result.date)
初期化の順番は変わらない
パラメータ付きのトレイトであっても、評価の順番については通常のトレイトと同様に処理されます。
パラメータが評価されるタイミングは、そのパラメータを持つトレイトが評価される直前です。
パラメータを評価して、そのパラメータを使用してトレイトを評価するという順番です。
パラメータ付きトレイトの制約
パラメータ付きトレイトは、以下の3つの制約に従って使用されます。 まとめると、パラメータ付きトレイトに対しては「どこかで必ず一度だけクラスからパラメータを渡す必要がある」ということです。
1. パラメータ付きトレイトにパラメータを渡すことができるのは、クラスだけである
パラメータ付きトレイトにパラメータを渡すことができるのは、クラスだけです。
また、トレイトはパラメータ付きトレイトに対してパラメータを渡すことはできません。
つまり以下のような実装はできないということです。
trait ClientError extends Result(418, "I'm a teapot")
2. あるクラスC
がパラメータ付きトレイトT
を実装しているが、C
のスーパークラスはT
を実装していない場合、C
はT
にパラメータを渡さなければならない
たとえば、ImATeapot
クラスがパラメータ付きトレイトResult
を実装しているとします。
もしImATeapot
のスーパークラスBadRequest
がResult
を実装していない場合、ImATeapot
はResult
にパラメータを渡さなければならない、ということです。
以下の例をご覧ください。
これは不正な例です。
Result
に対してどのクラスもパラメータを渡していないことがわかります。
class ImATeapot extends BadRequest with Result { def body = "Sorry, I'm a teapot." } class BadRequest
これはOKな例です。
class ImATeapot extends BadRequest with Result(418, "I'm a teapot") { def body = "Sorry, I'm a teapot." } class BadRequest
あるいはImATeapot
のかわりにBadRequest
にてトレイトパラメータを渡すということもできます。
3. あるクラスC
がパラメータ付きトレイトT
を実装していて、かつC
のスーパークラスもT
を実装している場合、CはTにパラメータを渡すことはできない
たとえば、ImATeapot
クラスがパラメータ付きトレイトResult
を実装しているとします。
このうえでさらにImATeapot
のスーパークラスBadRequest
もResult
を実装している場合には、ImATeapot
はResult
にパラメータを渡すことはできません。
これは不正な例です。
Result
に対して重複してパラメータを渡していることがわかります。
class ImATeapot extends BadRequest with Result(418, "I'm a teapot") { override def body = "Sorry, I'm a teapot." } class BadRequest extends Result(400, "Bad Request") { def body = s"$statusCode $statusCodeName" }
これはOKな例です。
class ImATeapot extends BadRequest with Result { override def body = "Sorry, I'm a teapot." } class BadRequest extends Result(400, "Bad Request") { def body = s"$statusCode $statusCodeName" }
あるいはBadRequest
のかわりにImATeapot
にてトレイトパラメータを渡すということもできます。